﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.ESB.GlobalPropertyContext;
using Microsoft.Practices.ESB.Resolver;
using Microsoft.Practices.ESB.Resolver.BRE;
using Microsoft.Practices.ESB.Resolver.Itinerary;
using Microsoft.Practices.ESB.Resolver.Itinerary.Facts;
using Microsoft.Practices.ESB.Exception.Management;
using Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Repository;
using Microsoft.Practices.ESB.Resolver.Itinerary.DataAccess;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.XLANGs.BaseTypes;
using System.Reflection;
using System.Runtime.InteropServices;

namespace BizTalk.ESB.Extensions.Resolvers
{
    public class ItineraryListResolution
    {
        public ItineraryListResolution()
        {
            this.Itineraries = new List<Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary>();
        }

        public String MessageType { get; set; }
        public String DocumentSpecStrongName { get; set; }
        public String DocumentSpecName { get; set; }
        public List<Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary> Itineraries { get; set; }

        private Int32 _ItineraryCount;
        public Int32 ItineraryCount
        {
            get
            {
                if (this.Itineraries != null)
                {
                    this._ItineraryCount = this.Itineraries.Count;
                }
                return this._ItineraryCount;
            }
            set
            {
                this._ItineraryCount = value;
            }
        }

        private String _ItineraryList;
        public String ItineraryList
        {
            get
            {
                if (this.Itineraries != null)
                {
                    StringBuilder sb = new StringBuilder();
                    foreach (Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary i in this.Itineraries)
                    {
                        String itineraryKey = i.name + "." + i.version;
                        sb.Append(itineraryKey.TrimEnd(".".ToCharArray()));
                        sb.Append(",");
                    }
                    String itineraryList = sb.ToString();
                    this._ItineraryList = itineraryList.TrimEnd(",".ToCharArray());
                }
                return this._ItineraryList;
            }
            set
            {
                this._ItineraryList = value;
                if (this.Itineraries.Count == 0)
                {
                    String[] itineraryArray = this._ItineraryList.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    foreach (String itineraryString in itineraryArray)
                    {
                        Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary i = new Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary();
                        String[] itineraryParts = itineraryString.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        if (itineraryParts.Length == 3)
                        {
                            i.name = itineraryParts[0];
                            i.version = itineraryParts[1] + itineraryParts[2];
                            this.Itineraries.Add(i);
                        }
                        else if (itineraryParts.Length > 0)
                        {
                            i.name = itineraryParts[0];
                            this.Itineraries.Add(i);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Call this from BRE.  For static, just set the ItineraryList directly
        /// </summary>
        /// <param name="ItineraryName"></param>
        /// <param name="ItineraryMajorVersion"></param>
        /// <param name="ItineraryMinorVersion"></param>
        public void AddItineraryWithVersion(String ItineraryName, Int32 ItineraryMajorVersion, Int32 ItineraryMinorVersion)
        {
            if (this.Itineraries == null)
            {
                this.Itineraries = new List<Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary>();
            }
            Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary i = new Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary();
            i.name = ItineraryName;
            i.version = ItineraryMajorVersion.ToString() + "." + ItineraryMinorVersion.ToString();
            this.Itineraries.Add(i);
        }

        public void AddItinerary(string ItineraryName)
        {
            if (this.Itineraries == null)
            {
                this.Itineraries = new List<Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary>();
            }
            Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary i = new Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary();
            i.name = ItineraryName;
            i.version = string.Empty;
            this.Itineraries.Add(i);
        }
    }

    public class ItineraryListResolutionManager
    {
        public static BRE CreateResolverDescriptor(String config, String resolver)
        {
            BRE breWrapper;

            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (resolver == null)
            {
                throw new ArgumentNullException("resolver");
            }

            Dictionary<String, String> facts = null;
            try
            {
                BRE breTemp = new BRE();
                breTemp.useMsg = false;
                facts = ResolverMgr.GetFacts(config, resolver);
                breTemp.policy = ResolverMgr.GetConfigValue(facts, true, "policy");
                breTemp.version = ResolverMgr.GetConfigValue(facts, false, "version");
                Boolean useMessage, recognizeMessage;
                breTemp.useMsgSpecified = Boolean.TryParse(
                    ResolverMgr.GetConfigValue(facts, false, "useMsg")
                    , out useMessage);
                breTemp.recognizeMessageFormatSpecified = Boolean.TryParse(
                    ResolverMgr.GetConfigValue(facts, false, "recognizeMessageFormat")
                    , out recognizeMessage);
                breTemp.useMsg = useMessage;
                breTemp.recognizeMessageFormat = recognizeMessage;

                breWrapper = breTemp;
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (facts != null)
                {
                    facts.Clear();
                    facts = null;
                }
            }

            return breWrapper;
        }

        public static void SetResolverDictionary(ItineraryListResolution resolution, Dictionary<String, String> dictionary, IRepositoryProvider repository)
        {
            try
            {
                dictionary.Add("Resolver.ItineraryList", resolution.ItineraryList);
                dictionary.Add("Resolver.ItineraryCount", resolution.ItineraryCount.ToString());

                // ADD CODE HERE TO GET ITINERARY XML AND PUT IN DICTIONARY
                foreach (Microsoft.Practices.ESB.Resolver.Itinerary.Facts.Itinerary i in resolution.Itineraries)
                {
                    if (String.IsNullOrEmpty(i.version))
                    {
                        String ItineraryXml = repository.GetItinerary(i.name);
                        dictionary.Add(i.name, ItineraryXml);
                    }
                    else
                    {
                        String[] versionInfo = i.version.Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                        Int32 majorVersion = Convert.ToInt32(versionInfo[0]);
                        Int32 minorVersion = Convert.ToInt32(versionInfo[1]);
                        String ItineraryXml = repository.GetItinerary(i.name, majorVersion, minorVersion);
                        System.Diagnostics.Trace.WriteLine("Itinerary XML: " + ItineraryXml);
                        dictionary.Add(i.name + "." + i.version, ItineraryXml);
                    }
                }
            }
            catch (Exception e)
            {
                System.Diagnostics.Trace.WriteLine("Error!!! " + e.Message);
            }
        }

        public static void SetContext(ItineraryListResolution resolution, IBaseMessage message, IPipelineContext pipelineContext)
        {
            if (pipelineContext == null)
            {
                throw new ArgumentNullException("pipelineContext");
            }
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            if (resolution == null)
            {
                throw new ArgumentNullException("resolution");
            }

            try
            {
                resolution.DocumentSpecStrongName = ((String)message.Context.Read(BtsProperties.SchemaStrongName.Name, BtsProperties.SchemaStrongName.Namespace)) ?? String.Empty;
                EventLogger.Write("Document strong name is {0} after first context read", new Object[] { resolution.DocumentSpecStrongName });
                resolution.MessageType = ((String)message.Context.Read(BtsProperties.MessageType.Name, BtsProperties.MessageType.Namespace)) ?? String.Empty;
                EventLogger.Write("Document message type is {0} after first context read", new Object[] { resolution.DocumentSpecStrongName });

                if (String.IsNullOrEmpty(resolution.DocumentSpecStrongName) == false)
                {
                    Int32 index = resolution.DocumentSpecStrongName.IndexOf(",", StringComparison.CurrentCultureIgnoreCase);
                    if ((index > 0) && (index < resolution.DocumentSpecStrongName.Length))
                    {
                        resolution.DocumentSpecName = resolution.DocumentSpecStrongName.Substring(0, index);
                    }
                    else
                    {
                        resolution.DocumentSpecName = resolution.DocumentSpecStrongName;
                    }

                    try
                    {
                        IDocumentSpec docSpecByName = pipelineContext.GetDocumentSpecByName(resolution.DocumentSpecStrongName);
                        if (docSpecByName != null)
                        {
                            resolution.DocumentSpecName = docSpecByName.DocSpecName;
                        }
                        if (String.IsNullOrEmpty(resolution.MessageType))
                        {
                            resolution.MessageType = docSpecByName.DocType;
                        }
                    }
                    catch (DocumentSpecException docspecEx)
                    {
                        System.Diagnostics.Trace.WriteLine("ERROR: " + docspecEx.Message);
                    }
                    catch (COMException comEx)
                    {
                        System.Diagnostics.Trace.WriteLine("ERROR" + comEx.Message);
                    }
                }
                EventLogger.Write("Document name is {0} after check.", new Object[] { resolution.DocumentSpecName });
                EventLogger.Write("Document strong name is {0} after check.", new Object[] { resolution.DocumentSpecStrongName });
                EventLogger.Write("Document message type is {0} after check.", new Object[] { resolution.MessageType });

            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
        }

        public static void SetContext(ItineraryListResolution resolution, XLANGMessage message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            if (resolution == null)
            {
                throw new ArgumentNullException("resolution");
            }
            try
            {
                resolution.MessageType = ((String)ItineraryListResolutionManager.GetMsgProperty(message
                    , typeof(BTS.MessageType))) ?? String.Empty;
                resolution.DocumentSpecStrongName = ((String)ItineraryListResolutionManager.GetMsgProperty(message
                    , typeof(BTS.SchemaStrongName))) ?? String.Empty;
                if (String.IsNullOrEmpty(resolution.DocumentSpecStrongName) == false)
                {
                    Int32 index = resolution.DocumentSpecStrongName.IndexOf(",", StringComparison.CurrentCultureIgnoreCase);
                    if ((index > 0) && (index < resolution.DocumentSpecStrongName.Length))
                    {
                        resolution.DocumentSpecStrongName = resolution.DocumentSpecStrongName.Substring(0, index);
                    }
                }
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
        }

        private static Object GetMsgProperty(XLANGMessage message, Type property)
        {
            Object prop;
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }
            if (property == null)
            {
                throw new ArgumentNullException("property");
            }
            Object PropertyValue = null;
            try
            {
                PropertyValue = message.GetPropertyValue(property);
                prop = PropertyValue;
            }
            catch (Exception e)
            {
                EventLogger.Write(MethodBase.GetCurrentMethod(), e);
                throw;
            }
            finally
            {
                if (PropertyValue != null)
                {
                    PropertyValue = null;
                }
            }
            return prop;
        }
    }
}
